#ifndef __OPERATION_H__
#define __OPERATION_H__

#include <QSharedDataPointer>
#include <QString>
#include <QVector>
#include "Complex.h"
#include "OperationData.h"

class Operation
{
public:
    /// Special uint value meaning variable argument list.
    static const uint VARIABLE_ARG_LIST = static_cast<uint>(-1);
    ///
    typedef Complex (* ProcedureType)(const OperationData::ArgumentList&);
    
    /// Exception thrown when an operation fails.
    class Exception : public std::exception
    {
    public:
        Exception(const QString& e) : err(e)
        { }
        virtual ~Exception() throw()
        { }
        virtual const char* what() const throw()
        { return err.toUtf8().data(); }
        const QString& qWhat() const throw()
        { return err; }
    private:
        const QString err;
    };
    
    /// Default constructor.
    Operation()
    { d = new OperationData(); }
	/// Full constructor.
    /// \note   Also adds the operation to the operation list.
	Operation(
		const QString& identifier,
        const QString& help_string,
        bool functional,
        uint precedence,
        uint num_args,
        ProcedureType procedure)
	{
        d = new OperationData();
        d->identifier = identifier;
        d->help_string = help_string;
        d->functional = functional;
        d->precedence = precedence;
        d->num_args = num_args;
        d->procedure = procedure;
        addOperation(*this);
    }
    /// Empty destructor.
    ~Operation()
    { }
	// Accessors
	const QString& identifier() const
	{ return d->identifier; }
	const QString& helpString() const
	{ return d->help_string; }
	bool functional() const
	{ return d->functional; }
	uint precedence() const
	{ return d->precedence; }
	uint numArgs() const
	{ return d->num_args; }
	const ProcedureType procedure() const
	{ return d->procedure; }
	// Modifiers
	void setIdentifier(const QString& identifier)
	{ d->identifier = identifier; }
	void setHelpString(const QString& help_string)
	{ d->help_string = help_string; }
	void setFunctional(bool functional)
	{ d->functional = functional; }
	void setPrecedence(uint precedence)
	{ d->precedence = precedence; }
	void setNumArgs(uint num_args)
	{ d->num_args = num_args; }
	void setProcedure(ProcedureType procedure)
	{ d->procedure = procedure; }
	/// Call the Operation's procedure.
	Complex call(const OperationData::ArgumentList& args) const
	{
		if (d->procedure) {
			return d->procedure(args);
		} else {
			return Complex();
		}
	}

	/// Static list of available Operations.
	static QVector<Operation>* operationList();
	/// Find Operation in the static Operation list.
	static const Operation& findOperation(
		const QString& identifier,
		uint num_args=VARIABLE_ARG_LIST,
		bool* found=0);
	///  Add Operation to the static Operation list.
	static void addOperation(const Operation& op);

private:
	QSharedDataPointer<OperationData> d;

};

#endif
